﻿package Util
{ 
	public class Vec
	{
		//*********************************************************************************	
		// Variables
		//*********************************************************************************	

		// Coordinate Points in 3D Vector Space
		public var x:Number, y:Number, z:Number = 0.0;
		
		// Special points in Vector Space
		public static var ZeroVector:Vec = new Vec(0, 0, 0);
		public static var UnitVectorX:Vec = new Vec(1, 0, 0);
		public static var UnitVectorY:Vec = new Vec(0, 1, 0);
		public static var UnitVectorZ:Vec = new Vec(0, 0, 1);
		public static var NegativeUnitVectorX:Vec = new Vec( -1, 0, 0);
		public static var NegativeUnitVectorY:Vec = new Vec(0, -1, 0);
		public static var NegativeUnitVectorZ:Vec = new Vec(0, 0, -1);
		public static var UnitScaleVector:Vec = new Vec(1, 1, 1);

		//*********************************************************************************	
		// Constructors
		//*********************************************************************************	

		// A mutliple constructor handler
		public function Vec(... args) 
		{
			if(args.length == 3) 
			{
				x = args[0]; 
				y = args[1]; 
				z = args[2];
			} 
			else if(args.length == 2) 
			{
				x = args[0]; 
				y = args[1];
				z = 0;
			} 
			else 
			{
				x = 0; 
				y = 0; 
				z = 0;		
			}
		}
		
		// This serves as an alternate Constructor
		// Returns a new Vectore3D instance
		public function Constructor():Vec
		{
			return new Vec(this.x, this.y, this.z);
		}
		
		// Serves as a Copy Constructor
		public function CopyConstructor(v:Vec):Vec
		{
			return new Vec(v.x, v.y, v.z);
		}

		//*********************************************************************************	
		// Operator Functions
		//*********************************************************************************	

		// Checks if a Vector is equal to another
		// Serves as an == operator
		public static function isEqual(lhs:Vec,rhs:Vec):Boolean
		{ 
			return (lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z);
		}

		// Checks if a Vector is not equal to another
		// Serves as an != operator
		public static function isNotEqual(lhs:Vec,rhs:Vec)
		{
			return (lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z);
		}
		
		// Returns true if the vector's scalar components are all greater
        // that the ones of the vector it is compared against.
		// Serves as a < operator
		public static function isLesser(lhs:Vec,rvec:Vec):Boolean
		{
			if (lhs.x < rvec.x && lhs.y < rvec.y && lhs.z < rvec.z)
			{
				return true;
			}
			return false;
		}

		// Returns true if the vector's scalar components are all smaller
        // than the ones of the vector it is compared against.
		// Serves as a > operator
		public static function isGreater(lhs:Vec,rhs:Vec):Boolean
		{
			if (lhs.x > rhs.x && lhs.y > rhs.y && lhs.z > rhs.z)
			{
				return true;
			}
			return false;
		}
		
		//*********************************************************************************	
		// Setter Functions
		//*********************************************************************************	
		
		
		public function setX(x2:Number=0.0):void
		{
			this.x = x2;
		}
		
		public function setY(y2:Number=0.0):void
		{
			this.y = y2;
		}
		
		public function setZ(z2:Number=0.0):void
		{
			this.z = z2;
		}
		
		public function getX():Number
		{
			return x;
		}
		
		public function getY():Number
		{
			return y;
		}
		
		public function getZ():Number
		{
			return z;
		}
		
		public function setXY(x2:Number=0.0,y2:Number=0.0):void
		{
			this.x = x2;
			this.y = y2;
		}
		
		public function setXYZ(x2:Number=0.0, y2:Number=0.0, z2:Number=0.0):void
		{
			this.x = x2;
			this.y = y2;
			this.z = z2;
		}
		
		public function v_setXYZ(v:Vec):void
		{
			this.x = v.x;
			this.y = v.y;
			this.z = v.z;
		}
		
		//*********************************************************************************		
		//  Utility Functions
		//*********************************************************************************		

		// Sets this vector's components to the minimum of its own and the
		// ones of the passed in vector.
		
		// 'Minimum' in this case means the combination of the lowest
		// value of x, y and z from both vectors. Lowest is taken just
		// numerically, not magnitude, so -1 < 0.
		public function MakeFloor(cmp:Vec):void
		{
			if (cmp.x < x)
			{
				x=cmp.x;
			}
			if (cmp.y < y)
			{
				y=cmp.y;
			}
			if (cmp.z < z)
			{
				z=cmp.z;
			}
		}
		
		// Sets this vector's components to the maximum of its own and the
        // ones of the passed in vector.
            
        // 'Maximum' in this case means the combination of the highest
        // value of x, y and z from both vectors. Highest is taken just
        // numerically, not magnitude, so 1 > -3.
		public function MakeCeil(cmp:Vec):void
		{
			if (cmp.x > x)
			{
				x=cmp.x;
			}
			if (cmp.y > y)
			{
				y=cmp.y;
			}
			if (cmp.z > z)
			{
				z=cmp.z;
			}
		}
		
		// Generates a vector perpendicular to this vector (eg an 'up' vector).
       
		// This method will return a vector which is perpendicular to this
		// vector. There are an infinite number of possibilities but this
		// method will guarantee to generate one of them. If you need more
		// control you should use the Quaternion class.
		public function Perpendicular():Vec
		{
			var fSquareZero:Number = 0.000001 * 0.000001;

			var perp:Vec = this.CrossProduct(Vec.UnitVectorX);

			// Check length
			if (perp.SquaredMagnitude() < fSquareZero)
			{
				//This vector is the Y axis multiplied by a scalar, so we have
  		 		//to use another axis.
				perp = this.CrossProduct(Vec.UnitVectorY);
			}

			return perp;
		}
		
		// Returns true if this vector is zero length.
		public function IsZeroLength():Boolean
		{
			var sqlen:Number = this.x * this.x + this.y * this.y + this.z * this.z;
			return sqlen < 0.000001 * 0.000001;
		}

		// As normalise, except that this vector is unaffected and the
        // normalised vector is returned as a copy.
		public function NormalisedCopy():Vec
		{
			var ret:Vec=new Vec(this);
			ret.fNormalize();
			return ret;
		}

		// Calculates a reflection vector to the plane with the given normal .
        // NB assumes 'this' is pointing AWAY FROM the plane, invert if it is not.
		public function Reflect(normal:Vec):Vec
		{
			return new Vec(VectorSubtraction1(ScalarMultiplication2(2, ScalarMultiplication2(DotProduct(normal), normal))));
		}
		
		// Calculates the Magnitude of the Vector
		public function Magnitude():Number
		{
			return Number(Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z));
		}
		
		// Calculates the Square of the Magnitude of a Vector
		public function SquaredMagnitude():Number
		{
			return this.x * this.x + this.y * this.y + this.z * this.z;
		}
		
		// Calculates the MidPoint of Vector
		public function VectorMidPoint(vec:Vec):Vec
		{
			return new Vec(this.x + vec.x * 0.5, this.y + vec.y * 0.5, this.z + vec.z * 0.5);
		}
		
		// Calculates the Normal
		public function fNormalize():void
		{
			var m:Number = 0.0;
			m = Magnitude();
			if (m > 0)
			{
				ScalarDivision1(m);
			}
		}
		
		// Calculates the limiting Values
		public function Limit(max:Number=0.0):void
		{
			if (Magnitude() > max)
			{
				fNormalize();
				ScalarMultiplication1(max);
			}
		}
		
		// Calculates a 2 Dimensional Angle of Direction
		public function angle2D():Number
		{
			var angle:Number = 0.0;
			angle = Math.atan2( -this.y, this.x);
			return -1 * angle;
		}
		
		// Calculates the Distance of a Vector from Another
		public function Distance(v1:Vec, v2:Vec):Number
		{
			var dx:Number = 0.0;
			dx=v1.x - v2.x;
			
			var dy:Number = 0.0;
			dy=v1.y - v2.y;
			
			var dz:Number = 0.0;
			dz=v1.z - v2.z;
			
			return Number(Math.sqrt(dx * dx + dy * dy + dz * dz));
		}
		
		// Calculates the dot (scalar) product of this vector with another.
        //
        // The dot product can be used to calculate the angle between 2
        // vectors. If both are unit vectors, the dot product is the
        // cosine of the angle; otherwise the dot product must be
        // divided by the product of the lengths of both vectors to get
        // the cosine of the angle. This result can further be used to
        // calculate the distance of a point from a plane.

		public function DotProduct(vec:Vec):Number
		{
			return this.x * vec.x + this.y * vec.y + this.z * vec.z;
		}
		
		// Calculates the cross-product of 2 vectors, i.e. the vector that
        // lies perpendicular to them both.
        
        // The cross-product is normally used to calculate the normal
        // vector of a plane, by calculating the cross-product of 2
        // non-equivalent vectors which lie on the plane (e.g. 2 edges
        // of a triangle).
        
        // Returns a vector which is the result of the cross-product. This
        // vector will <b>NOT</b> be normalised, to maximise efficiency
        // - call Vector3::normalise on the result if you wish this to
        // be done. As for which side the resultant vector will be on, the
        // returned vector will be on the side from which the arc from 'this'
        // to rkVector is anticlockwise, e.g. UNIT_Y.CrossProduct(UNIT_Z)
        // = UNIT_X, whilst UNIT_Z.CrossProduct(UNIT_Y) = -UNIT_X.
		// This is because PV3D uses a right-handed coordinate system.

		public function CrossProduct(rkVector:Vec):Vec
		{
			var kCross:Vec = new Vec();

			kCross.x=this.y * rkVector.z - this.z * rkVector.y;
			kCross.y=this.z * rkVector.x - this.x * rkVector.z;
			kCross.z=this.x * rkVector.y - this.y * rkVector.x;

			return kCross;
		}
		
		//**************************************************************************************		
		// Arithmetic Functions
		//**************************************************************************************
		
		// Serves as Vector Addition but takes on 1 argument
		public function VectorAddition1(v:Vec):Vec
		{
			this.x += v.x;
			this.y += v.y;
			this.z += v.z;
			return this;
		}
		
		// Same as above but takes two vectors as arguments
		public function VectorAddition2(v1:Vec, v2:Vec):Vec 
		{
    		var v:Vec = new Vec(v1.x + v2.x,v1.y + v2.y, v1.z + v2.z);
   			return v;
  		}
		
		// Serves as Vector Subtraction but takes on 1 argument
		public function VectorSubtraction1(v:Vec):void
		{
			this.x -= v.x;
			this.y -= v.y;
			this.z -= v.z;
		}
		
		// Same as above but takes two vectors as arguments
 	 	public function VectorSubtraction2(v1:Vec, v2:Vec):Vec 
		{
    		var v:Vec = new Vec(v1.x - v2.x,v1.y - v2.y,v1.z - v2.z);
    		return v;
  		}
		
		// This function does Scalar Multiplication and takes a 
		// scalar component as the argument
		public function ScalarMultiplication1(n:Number=0.0):Vec
		{
			this.x *= n;
			this.y *= n;
			this.z *= n;
			return this;
		}
		
		// This function is same as the above but takes two arguments
		// A scalar component and the Vector to multiply it with
		public function ScalarMultiplication2(n:Number,vec:Vec):Vec
		{
			vec.x *= n;
			vec.y *= n;
			vec.z *= n;
			
			return vec;
		}
				
		// This function does Scalar Division and takes a 
		// scalar component as the argument
		public function ScalarDivision1(n:Number):Vec
		{
			if (n == 0.0)
			{
				return new Vec();
			}
			this.x /= n;
			this.y /= n;
			this.z /= n;
			return this;
		}
		
		// This function is same as the above but takes two arguments
		// A scalar component and the Vector to divide it from
		public static function ScalarDivision2(lvec:Vec,fScalar:Number):Vec
		{
			var kDiv:Vec=new Vec;

			var fInv:Number=1.0 / fScalar;
			kDiv.x=lvec.x * fInv;
			kDiv.y=lvec.y * fInv;
			kDiv.z=lvec.z * fInv;

			return kDiv;
		}
	}
}